package com.zndroid.upgrader.downloader;

import androidx.annotation.NonNull;

import com.zndroid.upgrader.db.DBTaskImpl;
import com.zndroid.upgrader.db.TaskInfo;
import com.zndroid.upgrader.listener.IDownloadListener;
import com.zndroid.upgrader.utils.Constant;
import com.zndroid.upgrader.utils.FileUtil;
import com.zndroid.upgrader.utils.LogUtil;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 默认下载器，不依赖第三方，可实现 {@link IDownloadManager} 自定义实现下载过程
 * */
public class InnerDownloadManager implements IDownloadManager{
    private static final String TAG = Constant.TAG + "HttpDownloadManager";

    private boolean shutdown = false;
    private String apkUrl, apkName, downloadPath;
    private IDownloadListener listener;
    private ThreadPoolExecutor executor;
    private HttpURLConnection con;

    private long mMaxLength;
    private long mStartIndex;
    private long mCurrentIndex;

    private DBTaskImpl task;

    public InnerDownloadManager(String downloadPath, DBTaskImpl task) {
        this.downloadPath = downloadPath;
        this.task = task;
    }

    @Override
    public void startDownload(@NonNull String apkUrl, String apkName, @NonNull IDownloadListener listener) {
        this.apkUrl = apkUrl;
        this.apkName = apkName;
        this.listener = listener;

        download();
    }

    private void initHttp() {
        try {
            if (null != con)
                con.disconnect();//重置新的链接

            URL url = new URL(apkUrl);
            con = (HttpURLConnection) url.openConnection();
            con.setRequestMethod("GET");//TODO
            con.setReadTimeout(Constant.HTTP_TIME_OUT);
            con.setConnectTimeout(Constant.HTTP_TIME_OUT);
            //设置请求头 TODO add map
            con.setRequestProperty("Charsert", "UTF-8");
            con.setRequestProperty("Content-Type", "application/json; charset=UTF-8");//设置参数类型是json格式
            con.setRequestProperty("Connection", "Keep-Alive");
            con.setRequestProperty("Accept-Encoding", "identity");
        } catch (ProtocolException e) {
            listener.error(e);
            e.printStackTrace();
        } catch (MalformedURLException e) {
            listener.error(e);
            e.printStackTrace();
        } catch (IOException e) {
            listener.error(e);
            e.printStackTrace();
        }
    }

    private void download() {
        if (null != executor) {
            executor.shutdownNow();
            executor = null;
        }

        executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
            @Override
            public Thread newThread(@NonNull Runnable r) {
                Thread thread = new Thread(r);
                thread.setName(Constant.THREAD_NAME);

                return thread;
            }
        });
        executor.execute(runnable);
    }

    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            toDownload();
        }
    };

    private void toDownload() {
        if (isSupport_206()) {
            initHttp();
            superventionDownload();
        } else {
            deleteLocalAPK();
            initHttp();
            fullDownload();
        }
    }

    private boolean isSupport_206() {//检测是否支持断点续传 返回码 206 HttpURLConnection.HTTP_PARTIAL
        initHttp();
        con.setRequestProperty("range", "bytes=" + 1 + "-" + 2);
        try {
            return con.getResponseCode() == HttpURLConnection.HTTP_PARTIAL;
        } catch (IOException e) {
            return false;
        }
    }

    public void deleteLocalAPK() {
        //删除之前的安装包
        if (FileUtil.fileExists(downloadPath, apkName)) {
            FileUtil.delete(downloadPath, apkName);
        }
    }

    /**
     * 全部下载
     */
    private void fullDownload() {
        listener.start();

        try {
            if (con.getResponseCode() == HttpURLConnection.HTTP_OK) {
                InputStream is = con.getInputStream();
                int length = con.getContentLength();
                int len;
                //当前已下载完成的进度
                int progress = 0;
                byte[] buffer = new byte[1024 * 2];
                File file = FileUtil.createFile(downloadPath, apkName);
                FileOutputStream stream = new FileOutputStream(file);
                while ((len = is.read(buffer)) != -1 && !shutdown) {
                    //将获取到的流写入文件中
                    stream.write(buffer, 0, len);
                    progress += len;
                    listener.downloading(length, progress);
                }
                if (shutdown) {
                    //取消了下载 同时再恢复状态
                    shutdown = false;
                    LogUtil.d(TAG, "fullDownload: 取消了下载");
                    listener.cancel();
                } else {
                    listener.done(file);
                }
                //完成io操作,释放资源
                stream.flush();
                stream.close();
                is.close();
                //重定向
            } else if (con.getResponseCode() == HttpURLConnection.HTTP_MOVED_PERM ||
                    con.getResponseCode() == HttpURLConnection.HTTP_MOVED_TEMP) {
                apkUrl = con.getHeaderField("Location");
                con.disconnect();
                LogUtil.d(TAG, "fullDownload: 当前地址是重定向Url，定向后的地址：" + apkUrl);

                fullDownload();
            } else {
                listener.error(new SocketTimeoutException("下载失败：Http ResponseCode = " + con.getResponseCode()));
            }
            con.disconnect();
        } catch (Exception e) {
            listener.error(e);
            e.printStackTrace();
        }
    }

    /**
     * 断点续传
     * */
    private void superventionDownload() {
        listener.start();

        try {
            File localFile = FileUtil.createFile(downloadPath, apkName);

            if (!task.isTaskExists(apkUrl)) {
                TaskInfo info = new TaskInfo();
                info.setUrl(apkUrl);
                task.insertTask(info);
            }

            TaskInfo taskInfo = task.getTask(apkUrl);
            if (localFile.exists() && localFile.length() >= 0){
                mStartIndex = taskInfo.getStartIndex();
                con.setRequestProperty("range", "bytes=" + mStartIndex + "-");
            } else {
                mStartIndex = 0;
            }

            if (con.getResponseCode() == HttpURLConnection.HTTP_OK || con.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) {
                if (mStartIndex == 0) {
                    mMaxLength = con.getContentLength();
                    task.updateTask(apkUrl, mStartIndex, mMaxLength);
                } else {
                    mMaxLength = task.getTask(apkUrl).getFileSize();
                }

                InputStream is = con.getInputStream();
                RandomAccessFile raf = FileUtil.createRAFile(downloadPath, apkName);
                assert raf != null;
                raf.seek(mStartIndex);
                mCurrentIndex = mStartIndex;
                int len = 0;
                byte[] buf = new byte[1024];
                while ((len = is.read(buf)) != -1) {
                    if (shutdown) {
                        task.updateTask(apkUrl, raf.getFilePointer(), mMaxLength);
                        break;
                    }
                    raf.write(buf, 0, len);
                    mCurrentIndex += len;
                    listener.downloading(mMaxLength, raf.getFilePointer());
                }
                is.close();
                raf.close();
                if (mMaxLength - mCurrentIndex == 0 && localFile.length() == mMaxLength) {
                    listener.done(localFile);

                    reset();
                }

            } else if (con.getResponseCode() == HttpURLConnection.HTTP_MOVED_PERM ||
                    con.getResponseCode() == HttpURLConnection.HTTP_MOVED_TEMP) {//重定向
                apkUrl = con.getHeaderField("Location");
                con.disconnect();
                LogUtil.d(TAG, "superventionDownload: 当前地址是重定向Url，定向后的地址：" + apkUrl);

                superventionDownload();
            } else {
                listener.error(new SocketTimeoutException("下载失败：Http ResponseCode = " + con.getResponseCode()));

                reset();
            }
            con.disconnect();
        } catch (Exception e) {
            listener.error(e);
            reset();

            e.printStackTrace();
        }
    }

    private void reset() {
        if (null != task) task.deleteTask(apkUrl);
        mMaxLength = 0;
        mStartIndex = 0;
    }

    @Override
    public void stopDownload() {
        shutdown = true;
        executor.shutdownNow();
        if (null != con) con.disconnect();
    }

    @Override
    public void pause() {
        shutdown = true;
    }

    @Override
    public void continues() {
        if (mCurrentIndex == mMaxLength) {
            mStartIndex = 0;
        }
        shutdown = false;
        download();
    }
}
